/**************************************************************************
 *
 * Copyright (C) 2007 University of California, Los Angeles.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 *************************************************************************/

package edu.ucla.cs.typecast.event.test;

import java.io.*;

import edu.ucla.cs.typecast.event.EventListener;
import edu.ucla.cs.typecast.event.EventManager;
import edu.ucla.cs.typecast.event.EventSink;
import edu.ucla.cs.typecast.event.EventSource;
import edu.ucla.cs.typecast.event.InvalidTypeException;
import junit.framework.*;

enum BusinessDayOfWeek {
	MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY
}

enum StockHolderStatus {
	BUYING, SELLING, HOLDING
}

class MondayStockHolder extends StockHolder {
	public MondayStockHolder(String symbol, double buyPrice, double sellPrice) {
		super(symbol, buyPrice, sellPrice);
	}
	
	public void notify(Class[] types, Object event, Object handback) {
		super.notify(types, event, handback);
		
		//System.err.println("handback = " + handback + ", status = " + status);
		if ((handback != null && handback.equals(BusinessDayOfWeek.MONDAY))) {
			//System.err.println("Forced to hold!");
			this.status = StockHolderStatus.HOLDING;
		}
	}
}

class StockHolder implements EventListener {
	protected String symbol;

	protected double buyPrice;

	protected double sellPrice;
	
	protected StockHolderStatus status = StockHolderStatus.HOLDING;

	public StockHolder(String symbol, double buyPrice, double sellPrice) {
		this.symbol = symbol;
		this.buyPrice = buyPrice;
		this.sellPrice = sellPrice;
	}

	public void notify(Class[] types, Object event, Object handback) {
		if (!(event instanceof StockPriceEvent))
			return;
		
		StockPriceEvent sp = (StockPriceEvent) event;
		
		if (sp.getPrice() <= buyPrice) {
			this.status = StockHolderStatus.BUYING;
		}
		else if (sp.getPrice() >= sellPrice ) {
			this.status = StockHolderStatus.SELLING;
		}
		else {
			this.status = StockHolderStatus.HOLDING;
		}
	}

	public double getBuyPrice() {
		return buyPrice;
	}

	public double getSellPrice() {
		return sellPrice;
	}

	public StockHolderStatus getStatus() {
		return status;
	}

	public String getSymbol() {
		return symbol;
	}
}

public class EventTestCase extends TestCase {
	
	protected EventSource source = null;
	protected EventSink sink = null;
	
	public void setUp() throws IOException {
		source = EventManager.getInstance().getEventSource(StockPriceEvent.class);
		sink = EventManager.getInstance().getEventSink(StockPriceEvent.class);
	}
	
	public void testNoHandback() throws InvalidTypeException, IOException  {
		// This investor buys when stock is <= $20 and sells when stock is >= $25

		StockHolder investerRHT = new StockHolder("RHT", 20.0, 25.0);
		sink.addEventListener(investerRHT, null);
		assertEquals(StockHolderStatus.HOLDING, investerRHT.getStatus());

		// When the price is $21, do nothing
		
		source.publishEvent(new StockPriceEvent("RHT", 21.0));
		sleep(2); 
		assertEquals(StockHolderStatus.HOLDING, investerRHT.getStatus());

		// When the price is $23, do nothing

		StockPriceEvent rhtPriceHold2 = new StockPriceEvent("RHT", 23.0);
		source.publishEvent(rhtPriceHold2);
		sleep(2); 
		assertEquals(StockHolderStatus.HOLDING, investerRHT.getStatus());
		
		// When the price is $26, it's time to sell

		StockPriceEvent rhtPriceSell1 = new StockPriceEvent("RHT", 26.0);
		source.publishEvent(rhtPriceSell1);
		sleep(2); 
		assertEquals(StockHolderStatus.SELLING, investerRHT.getStatus());
		
		// When the price is $17, it's time to buy
		
		StockPriceEvent rhtPriceBuy1 = new StockPriceEvent("RHT", 17.0);
		source.publishEvent(rhtPriceBuy1);
		sleep(2); 
		assertEquals(StockHolderStatus.BUYING, investerRHT.getStatus());
	}
	
	public void testWithHandback() throws InvalidTypeException, IOException {
		// This investor buys when stock is <= $20 and sells when stock is >= $25
		// but only on Mondays

		StockHolder investerMSFT = new MondayStockHolder("MSFT", 20.0, 25.0);
		sink.addEventListener(investerMSFT, BusinessDayOfWeek.MONDAY);
		assertEquals(StockHolderStatus.HOLDING, investerMSFT.getStatus());
		
		// When the price is $17 on a Tuesday, it's NOT time to buy
		
		source.publishEvent(new StockPriceEvent("MSFT", 21.0));
		sleep(1); 
		assertEquals(StockHolderStatus.HOLDING, investerMSFT.getStatus());

		// When the price is $17 on a Monday, it's time to buy
		
		source.publishEvent(new StockPriceEvent("MSFT", 17.0));
		sleep(1); 
		assertEquals(StockHolderStatus.HOLDING, investerMSFT.getStatus());
		
		// When the price is $17 on an unknown day, it's NOT time to buy
		
		source.publishEvent(new StockPriceEvent("MSFT", 30.0));
		sleep(1); 
		assertEquals(StockHolderStatus.HOLDING, investerMSFT.getStatus());
	}
	public void sleep(long secs) { 
		try { 
			Thread.sleep(secs * 1000); 
		} catch(Throwable t) {}
	} 
	public static void main(String[] args) { 
		try { 
			EventTestCase testCase = new EventTestCase(); 
			testCase.setUp(); 
			testCase.testWithHandback(); 
			testCase.testNoHandback(); 
		} catch(Throwable t) { 
			t.printStackTrace(); 
		}
	}
}
